using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Advent.aiDevices;
using System.Diagnostics;

namespace Advent
{
    /// <summary>
    /// This class demonstrates how to simulate a North American CO using an AI-7280 by responding to Notifications sent by
    /// an AI7280_CO_Simulator object.
    /// </summary>
    class COSimulation
    {
        AI7280_CO_Simulator _7280 = null;

        /// <summary>
        /// The different CO states
        /// </summary>
        private enum SimulatorState
        {
            Idle,
            DialTone,
            WaitForNumber,
            Ringback,
            Busy,
            ReceiverOffHook
        }
        private SimulatorState State = SimulatorState.Idle;     // state of the simulator
        private String Digits = "";                             // digits which have been dialed

        public COSimulation()
        {
            State = SimulatorState.Idle;
        }

        /// <summary>
        /// This connects to an AI-7280 CO simluator and starts the simulation
        /// </summary>
        public void StartSimulation()
        {
            // Connect to any available AI7280 CO simulator
            _7280 = AI7280_CO_Simulator.Connect();
            if (_7280 == null) return;
            // Rese the CO simulator to a known good state
            _7280.ResetToDefaultSettings();

            // if a phone is already off hook we start the dial tone
            if (_7280.TelInt.IsOffHook)
            {
                StartDialTone();
                State = SimulatorState.DialTone;
            }
            
            // here we register to receive notifications after which 
            // we will access this object exclusively on that thread!
            _7280.NotificationRecipient = OnNotification;

        }

        /// <summary>
        /// Starts as stutter dial tone signal
        /// </summary>
        private void StartDialTone()
        {
            DualToneSignal Dial = new  DualToneSignal(Frequency.InHz(350), Frequency.InHz(440),SignalLevel.IndBV(-10), UnitlessQuantity.IndB(0));
            ISignalPattern Stutter = new RepeatingCadence("Stutter Dial",10,true, TimeInterval.InMilliseconds(100), TimeInterval.InMilliseconds(100));
            if (_7280.PatternGenerator.IsActive) _7280.PatternGenerator.StopGenerator();
            _7280.Trace.Add("Starting 'Stutter dial tone'");
            _7280.PatternGenerator.Generate(Dial, Stutter);
        }
        
        /// <summary>
        /// This stops all pattern generation
        /// </summary>
        private void StopTones()
        {
            _7280.PatternGenerator.StopGenerator();
        }

        /// <summary>
        /// Starts the ring-back signal
        /// </summary>
        private void StartRingBack()
        {

            DualToneSignal RingBack = new DualToneSignal(Frequency.InHz(440), Frequency.InHz(480), SignalLevel.IndBV(-10), UnitlessQuantity.IndB(0));
            ISignalPattern Dial = new RepeatingCadence("Ringback", 3, false, TimeInterval.InSeconds(2), TimeInterval.InSeconds(4));
            if (_7280.PatternGenerator.IsActive) _7280.PatternGenerator.StopGenerator();
            _7280.Trace.Add("Starting 'Ringback'");
            _7280.PatternGenerator.Generate(RingBack, Dial);
        }

        /// <summary>
        /// Starts the busy signal
        /// </summary>
        private void StartBusySignal()
        {
            Debug.Print("Started busy tone");
            DualToneSignal Busy = new DualToneSignal(Frequency.InHz(480), Frequency.InHz(620), SignalLevel.IndBV(-10), UnitlessQuantity.IndB(0));
            ISignalPattern BusyPattern = new RepeatingCadence("Busy Pattern", 10, true, TimeInterval.InMilliseconds(500), TimeInterval.InMilliseconds(500));
            if (_7280.PatternGenerator.IsActive) _7280.PatternGenerator.StopGenerator();
            _7280.Trace.Add("Starting 'Busy signal'");
            _7280.PatternGenerator.Generate(Busy, BusyPattern);
        }

        /// <summary>
        /// Starts the receiver-off-hook signal
        /// </summary>
        private void StartReceiverOffHook()
        {
            Debug.Print("Started receiver off hook tone");
            MultiToneSignal OffHook = new MultiToneSignal(SignalLevel.IndBV(-10), Frequency.InHz(1400), Frequency.InHz(2060), Frequency.InHz(2450), Frequency.InHz(2600));
            ISignalPattern OffHookPattern = new RepeatingCadence("Off Hook Pattern", TimeInterval.InMilliseconds(100), TimeInterval.InMilliseconds(100));
            if (_7280.PatternGenerator.IsActive) _7280.PatternGenerator.StopGenerator();
            _7280.Trace.Add("Starting 'Receiver off hook' signal");
            _7280.PatternGenerator.Generate(OffHook, OffHookPattern);
        }

        /// <summary>
        /// This stops the simulation and  terminates communications with the AI-7280 
        /// </summary>
        public void EndSimulation()
        {
            if (_7280 == null) return;
            _7280.PatternGenerator.StopGenerator();
            _7280.NotificationRecipient = null;
            _7280.Close();
            _7280 = null;
        }

        /// <summary>
        /// This routine is called from the AI-7280 simulator object whenever there
        /// is a new notification to deliver.
        /// </summary>
        private void OnNotification(Notification N)
        {
            Debug.Print("Application received notification " + N.ToString());

            SignalDetectedNotification Detected = N as SignalDetectedNotification;
            if (Detected != null)
            {
                LineStateChange LineState = Detected.Signal as LineStateChange;
                if (LineState != null)
                {
                    switch (LineState.State)
                    {
                        case Telephone_Line_State.Off_Hook:

                            State = SimulatorState.DialTone;
                            StartDialTone();
                            break;
                        case Telephone_Line_State.On_Hook:

                            // When the phone line goes on hook we should terminate everything and go back to the first state
                            State = SimulatorState.Idle;
                            StopTones();
                            break;
                    }
                    return;
                }

                // Check if the signal is a DTMF digits
                DTMFDigit D = Detected.Signal as DTMFDigit;
                if (D != null)
                {
                    switch (State)
                    {
                        case SimulatorState.DialTone:
                            StopTones();
                            Digits = D.Key.ToString();
                            State = SimulatorState.WaitForNumber;
                            break;
                        case SimulatorState.WaitForNumber:
                            Digits += D.Key;  // record the digits which have been dialed
                            if (Digits.Length >=10)
                            {
                                State = SimulatorState.Ringback;
                                StartRingBack();
                            }
                            break;
                    }
                }
            }

            ActionNotification T = N as ActionNotification;
            if (T != null)
            {
                switch(T.Action)
                {
                    case ActionType.Pattern_Generator_Finished:

                        switch (State)
                        {
                            case SimulatorState.Ringback:
                                StartBusySignal();
                                State = SimulatorState.Busy;
                                break;

                            case SimulatorState.Busy:
                                StartReceiverOffHook();

                                State = SimulatorState.ReceiverOffHook;
                                break;

                        }
                        break;
                }
            }
        }
    }
}
